package cnooc.dataAnalysis.service.impl;


import cnooc.common.config.InfluxDbComUtil;
import cnooc.dataAnalysis.domain.AnalysisParam;
import cnooc.dataAnalysis.service.StatisticsService;
import cnooc.energy.domain.DeviceDataLocation;
import cnooc.energy.domain.Equipment;
import cnooc.energy.mapper.DeviceDataLocationMapper;
import cnooc.energy.mapper.EquipmentMapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cnooc.dataAnalysis.service.StatisticsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 统计数据MService业务层处理
 *
 * @author 韩是非
 * @date 2023-11-28
 */
@Service
@Slf4j
public class StatisticsServiceImpl implements StatisticsService {


    @Resource
    InfluxDbComUtil influxDbComUtil;
    @Resource
    EquipmentMapper equipmentMapper;
    @Resource
    DeviceDataLocationMapper deviceDataLocationMapper;

    private static final String xDataName = "xData";
    private static final String yDataName = "yData";
    private static final String totalDataName = "total";
    private static final DateTime initDateTime = DateUtil.parseDate("2023-01-01");
    private static boolean isTotal = false;

    private static final String getStatisticalDataSQL = "select sum(difference) as num from ( select DIFFERENCE({}) from {} where time >'{}' and time <'{}' tz('Asia/Shanghai')) where time >'{}' and time <'{}' GROUP BY time({}) fill(0) tz('Asia/Shanghai')";
    private static final String getStatisticalDataSQLByYear = "select DIFFERENCE(num) as num from (SELECT FIRST({}) AS num FROM {} WHERE time >= '{}' tz('Asia/Shanghai')) ,(SELECT last({}) AS num FROM {} WHERE time < '{}' tz('Asia/Shanghai')) ";
    private static final String getStatisticalDataSQLTotal = "select {} as num from  {} order by  time desc limit 1 tz('Asia/Shanghai') ";

    private Set<String> smartswitch3LocationList = new HashSet<>(Arrays.asList("yc97"));
//    private Set<String> smartswitch3LocationList = new HashSet<>(Arrays.asList("yc97","yc99","yc101","yc100","yc102","yc106"));

    @Override
    public Map queryStatistics(AnalysisParam param) {
        Long deviceId = param.getDeviceId();
        //设备信息
        Equipment equipment = equipmentMapper.selectEquipmentById(deviceId);
        //设备对应表名
        String tableName = equipment.getTableName();
        Long locationId = param.getLocationId();
        //点位信息
        DeviceDataLocation deviceDataLocation = deviceDataLocationMapper.selectDeviceDataLocationById(locationId);
        //位置
        String location = deviceDataLocation.getLocation();
        //点位对应的点位
        Long correspond = deviceDataLocation.getCorrespond();
        //对点关系
        String calclation = deviceDataLocation.getCalclation();
        //变比系数
        BigDecimal variationRatioCoefficient = deviceDataLocation.getVariationRatioCoefficient();
        Map<String, Object> statisticalDataMap = getStatisticalData(param,tableName,location);

        //对应点位和对点关系不为空时 进行数据操作
        if (correspond!=null&&StrUtil.isNotEmpty(calclation)){
            //获取对应点位的点位信息
            DeviceDataLocation correspondPointLocat = deviceDataLocationMapper.selectDeviceDataLocationById(correspond);
            //获取对应点位的数据
            Map<String, Object> correspondPointData = getStatisticalData(param, tableName,correspondPointLocat.getLocation());
            Map<String, Object> result = new LinkedHashMap<>();

            if (isTotal){
                BigDecimal first = Convert.toBigDecimal(statisticalDataMap.get(totalDataName));
                BigDecimal second = Convert.toBigDecimal(correspondPointData.get(totalDataName));
                String operation = numOperation(first, second, calclation, variationRatioCoefficient);
                result.put(totalDataName, operation);
                isTotal = false;
                return result;
            }

            List<String> xData = convertToCollection(statisticalDataMap.get(xDataName));
            List<String> yData = convertToCollection(statisticalDataMap.get(yDataName));
            List<String> correspondPointyData = convertToCollection(correspondPointData.get(yDataName));
            List<String> yTotolData = CollUtil.newArrayList();
            int i = 0;
            for (int i1 = 0; i1 < xData.size(); i1++) {
                BigDecimal value = Convert.toBigDecimal(yData.get(i));
                BigDecimal correspondPointValue = Convert.toBigDecimal(correspondPointyData.get(i));
                String operation = numOperation(value, correspondPointValue, calclation, variationRatioCoefficient);
                yTotolData.add(operation);
                i++;
            }
            result.put(xDataName, xData);
            result.put(yDataName, yTotolData);
            return result;
        }
        isTotal = false;
        return statisticalDataMap;
    }

    private Map<String, Object> getStatisticalData(AnalysisParam analysisParam,String tableName,String location){
        //数据维度
        Integer dimensions = analysisParam.getDimensions();
        //查询日期
        Date datetime = analysisParam.getDatetime();
        DateTime startTime = null;
        DateTime endTime = null;
        String executeSQL = "";
        Integer offset = 1;
        Integer key = 1;
        Map<String, Object> statisticalDataMap = new LinkedHashMap<>();
        List<String> xData = CollUtil.newArrayList();
        List<String> yData = CollUtil.newArrayList();
        switch (dimensions)
        {
            case 1://当日每小时
                startTime = DateUtil.beginOfDay(datetime);
                endTime = DateUtil.offset(startTime, DateField.DAY_OF_MONTH, offset);
                executeSQL = StrUtil.format(getStatisticalDataSQL, location, tableName, startTime, endTime, startTime, endTime,"1h");
                key = 0;
                break;
            case 2://当月每日
                startTime = DateUtil.beginOfMonth(datetime);
                endTime = DateUtil.offset(startTime, DateField.MONTH, offset);
                executeSQL = StrUtil.format(getStatisticalDataSQL, location, tableName, startTime, endTime, startTime, endTime,"1d");
                break;
            case 3://当年每月
                startTime = DateUtil.beginOfYear(datetime);
                statisticalDataMap = getMonthOrYearData(startTime, 12, key,DateField.MONTH, location,tableName);
                break;
            case 4://每年
                startTime = initDateTime;
                statisticalDataMap = getMonthOrYearData(startTime, DateUtil.year(datetime), 2023,DateField.YEAR, location,tableName);
                break;
            case 5://累计
                executeSQL = StrUtil.format(getStatisticalDataSQLTotal, location,tableName);
                isTotal = true;
                break;
        }
        if (StrUtil.isNotEmpty(executeSQL)){
            List<Map<String, Object>> query = influxDbComUtil.query(executeSQL);
            for (Map<String, Object> map : query) {
                xData.add(key.toString()); //1
                yData.add(NumberUtil.roundStr(map.get("num").toString(), 2, RoundingMode.CEILING));
                key++;
            }
            if (isTotal){
                if (smartswitch3LocationList.contains(location)&&"tb_donggu_smartswitch3".equals(tableName)){
                    String formatSQL = StrUtil.format(getStatisticalDataSQLTotal, location,"tb_donggu_smartswitch1");
                    List<Map<String, Object>> lastDataList = influxDbComUtil.query(formatSQL);
                    BigDecimal lastData = Convert.toBigDecimal(lastDataList.get(0).get("num"));
                    statisticalDataMap.put(totalDataName, (NumberUtil.roundStr((Convert.toBigDecimal(yData.get(0)).add(lastData)).toString(), 2, RoundingMode.CEILING)));
                } else {
                    statisticalDataMap.put(totalDataName, yData.get(0));
                }
            } else {
                statisticalDataMap.put(xDataName, xData);
                statisticalDataMap.put(yDataName, yData);
            }
        }
        return statisticalDataMap;
    }

    private Map<String, Object> getMonthOrYearData(DateTime startTime,Integer dataTime,Integer key,DateField dateField,String location,String tableName){
        DateTime endTime;
        int offset = 1;
        String format = "";
        Map<String,Object> statisticalDataMap = new LinkedHashMap<>();
        List<String> xData = CollUtil.newArrayList();
        List<String> yData = CollUtil.newArrayList();
        for (int i = key; i <= dataTime; i++) {
            endTime = DateUtil.offset(startTime, dateField, offset);
            format = StrUtil.format(getStatisticalDataSQLByYear, location, tableName, startTime,location, tableName,endTime);
            List<Map<String, Object>> query = influxDbComUtil.query(format);
            xData.add(key.toString());
            if (query.size()==0){
                yData.add("0.00");
            }
            for (Map<String, Object> map : query) {
                //智能电表进行了换表 从1换成了3 2024年的数据存在错误的情况 时序性数据库的值不能更改 需要手动修改值
                ZonedDateTime zonedDateTime = ZonedDateTime.parse(map.get("time").toString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
                DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String time = zonedDateTime.format(targetFormatter);
                //判断要查询的年份是否为2024年 同时点位是否需要累加计算
                if (key==2024&&DateUtil.parseDateTime(time).year()==2024&&smartswitch3LocationList.contains(location)&&"tb_donggu_smartswitch3".equals(tableName)){
                    String formatSQL = StrUtil.format(getStatisticalDataSQLTotal, location,"tb_donggu_smartswitch1");
                    List<Map<String, Object>> lastDataList = influxDbComUtil.query(formatSQL);
                    BigDecimal lastData = Convert.toBigDecimal(lastDataList.get(0).get("num"));
                    yData.add(NumberUtil.roundStr((Convert.toBigDecimal(map.get("num")).add(lastData)).toString(), 2, RoundingMode.CEILING));
                } else {
                    yData.add(NumberUtil.roundStr(map.get("num").toString(), 2, RoundingMode.CEILING));
                }
                break;
            }
            startTime = endTime;
            key++;
        }
        statisticalDataMap.put(xDataName, xData);
        statisticalDataMap.put(yDataName, yData);
        return statisticalDataMap;
    }

    private BigDecimal operation(String correspondRelation,BigDecimal first,BigDecimal second){
        BigDecimal result = BigDecimal.ZERO;
        switch (correspondRelation){
            case "add":
                result = first.add(second);
                break;
            case "subtract":
                result = first.subtract(second);
                break;
            case "multiply":
                result = first.multiply(second);
                break;
            case "divide":
                result = first.divide(second,2, RoundingMode.CEILING);
                break;
        }
        return result;
    }

    /**
     * 将Object类型转换为List<String>集合
     * @param o
     * @return
     */
    private List<String> convertToCollection(Object o){
        return Convert.convert(new TypeReference<List<String>>() {}, o);
    }

    /**
     * 数值运算
     * @param first
     * @param second
     * @param calclation
     * @param variationRatioCoefficient 变比系数
     * @return
     */
    private String numOperation(BigDecimal first,BigDecimal second,String calclation,BigDecimal variationRatioCoefficient){
        BigDecimal operation = operation(calclation, first, second);
        //存在系数则做乘法运算
        if (ObjectUtil.isNotNull(variationRatioCoefficient)){
            operation = operation("multiply", operation, variationRatioCoefficient);
        }
        return Convert.toStr(operation);
    }

    @Override
    public Map<String, Object> hourByDayCurve(String tableName, String location, Date datetime) {
        AnalysisParam analysisParam = new AnalysisParam();
        analysisParam.setDimensions(1);
        analysisParam.setDatetime(datetime);
        return getStatisticalData(analysisParam,tableName,location);
    }

    @Override
    public Map<String, Object> dayByMonthCurve(String tableName, String location, Date datetime) {
        AnalysisParam analysisParam = new AnalysisParam();
        analysisParam.setDimensions(2);
        analysisParam.setDatetime(datetime);
        return getStatisticalData(analysisParam,tableName,location);
    }

    @Override
    public Map<String, Object> monthByYearCurve(String tableName, String location, Date datetime) {
        AnalysisParam analysisParam = new AnalysisParam();
        analysisParam.setDimensions(3);
        analysisParam.setDatetime(datetime);
        return getStatisticalData(analysisParam,tableName,location);
    }

    @Override
    public Map<String, Object> yearByCurve(String tableName, String location, Date datetime) {
        AnalysisParam analysisParam = new AnalysisParam();
        analysisParam.setDimensions(4);
        analysisParam.setDatetime(datetime);
        return getStatisticalData(analysisParam,tableName,location);
    }
}
